package com.tdcy.workflow.controller;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.zip.ZipInputStream;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.FormService;
import org.activiti.engine.IdentityService;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.spring.ProcessEngineFactoryBean;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.tdcy.framework.bean.PageInfo;
import com.tdcy.framework.util.WebUtils;
import com.tdcy.sys.service.bean.LoginInfoBean;
import com.tdcy.sys.util.BusiContextExt;
import com.tdcy.workflow.entity.ActProcessDefinition;
import com.tdcy.workflow.service.WorkFlowService;
import com.tdcy.workflow.service.WorkflowTraceService;
import com.tdcy.workflow.util.WorkflowUtils;

@Controller
@RequestMapping(value = "/workflow")
public class ActivitiController {
	protected Logger logger = LoggerFactory.getLogger(getClass());

	@Autowired
	private RepositoryService repositoryService;
	@Autowired
	private FormService formService;
	@Autowired
	private IdentityService identityService;
	@Autowired
	private TaskService tasksService;
	@Autowired
	private RuntimeService runtimeService;
	@Autowired
	ProcessEngineConfiguration processEngineConfiguration;
	@Autowired
	ProcessEngineFactoryBean processEngine;
	@Autowired
	WorkflowTraceService traceService;
	@Autowired
	WorkFlowService workFlowService;

	/**
	 * 流程定义列表
	 *
	 * @return
	 */
	@ResponseBody
	@RequestMapping(value = "/getProcessList")
	public Object processList(HttpServletRequest request) {
		List<ActProcessDefinition> objects = new ArrayList<ActProcessDefinition>();

		ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery().orderByDeploymentId().desc();
		List<ProcessDefinition> processDefinitionList = processDefinitionQuery.list();
		for (ProcessDefinition processDefinition : processDefinitionList) {
			String deploymentId = processDefinition.getDeploymentId();
			Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(deploymentId).singleResult();
			ActProcessDefinition actdef = new ActProcessDefinition();
			actdef.setId(processDefinition.getId());
			actdef.setDeploymentId(deploymentId);
			actdef.setName(processDefinition.getName());
			actdef.setKey(processDefinition.getKey());
			actdef.setVersion(processDefinition.getVersion() + "");
			actdef.setResourceName(processDefinition.getResourceName());
			actdef.setDiagramResourceName(processDefinition.getDiagramResourceName());
			// actdef.setDeploymentTime(DateSupportUtils.date2str(deployment.getDeploymentTime()));
			actdef.setSuspended(processDefinition.isSuspended());
			objects.add(actdef);
		}
		return WebUtils.createJSONSuccess("获取成功", objects);
	}

	@RequestMapping(value = "/readProcessDefinition")
	public void resourceRead(@RequestParam("processDefinitionId") String processDefinitionId, @RequestParam("resourceType") String resourceType, HttpServletResponse response) throws Exception {
		ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
		String resourceName = "";
		if (resourceType.equals("image")) {
			resourceName = processDefinition.getDiagramResourceName();
			InputStream resourceAsStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), resourceName);
			try {
				response.setContentType("image/png");
				byte[] b = new byte[1024];
				int len = -1;
				while ((len = resourceAsStream.read(b, 0, 1024)) != -1) {
					response.getOutputStream().write(b, 0, len);
				}
				response.getOutputStream().flush();
			} catch (IOException e) {
				e.printStackTrace();
			}
		} else if (resourceType.equals("xml")) {
			resourceName = processDefinition.getResourceName();
			InputStream resourceAsStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), resourceName);
			try {
				response.setContentType("text/xml;charset=UTF-8");
				byte[] b = new byte[1024];
				int len = -1;
				while ((len = resourceAsStream.read(b, 0, 1024)) != -1) {
					response.getOutputStream().write(b, 0, len);
				}
				response.getOutputStream().flush();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 挂起、激活流程实例
	 */
	@ResponseBody
	@RequestMapping(value = "/updateProcessState")
	public Object updateProcessState(@RequestParam("state") String state, @RequestParam("processDefinitionId") String processDefinitionId) {
		if (state.equals("active")) {
			repositoryService.activateProcessDefinitionById(processDefinitionId, true, null);
			return WebUtils.createJSONSuccess("激活成功");
		} else if (state.equals("suspend")) {
			repositoryService.suspendProcessDefinitionById(processDefinitionId, true, null);
			return WebUtils.createJSONSuccess("挂起成功");
		}else{
			return WebUtils.createJSONError("参数有误");
		}
	}
	
	@ResponseBody
	@RequestMapping(value = "/convertToModel")
	public Object convertToModel(@RequestParam("processDefinitionId") String processDefinitionId) throws UnsupportedEncodingException, XMLStreamException {
		ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
		InputStream bpmnStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getResourceName());
		XMLInputFactory xif = XMLInputFactory.newInstance();
		InputStreamReader in = new InputStreamReader(bpmnStream, "UTF-8");
		XMLStreamReader xtr = xif.createXMLStreamReader(in);
		BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);

		BpmnJsonConverter converter = new BpmnJsonConverter();
		com.fasterxml.jackson.databind.node.ObjectNode modelNode = converter.convertToJson(bpmnModel);
		Model modelData = repositoryService.newModel();
		modelData.setKey(processDefinition.getKey());
		modelData.setName(processDefinition.getResourceName());
		modelData.setCategory(processDefinition.getDeploymentId());

		ObjectNode modelObjectNode = new ObjectMapper().createObjectNode();
		modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, processDefinition.getName());
		modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
		modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, processDefinition.getDescription());
		modelData.setMetaInfo(modelObjectNode.toString());

		repositoryService.saveModel(modelData);

		repositoryService.addModelEditorSource(modelData.getId(), modelNode.toString().getBytes("utf-8"));

		return WebUtils.createJSONSuccess("转换成功");
	}

	/**
	 * 删除部署的流程，级联删除流程实例
	 *
	 * @param deploymentId
	 *            流程部署ID
	 */
	@ResponseBody
	@RequestMapping(value = "/deleteProcess")
	public Object deleteProcess(@RequestParam("deploymentId") String deploymentId) {
		repositoryService.deleteDeployment(deploymentId, true);
		return WebUtils.createJSONSuccess("删除成功");
	}

	@ResponseBody
	@RequestMapping(value = "/deployProcessByFile")
	public Object deployProcessByFile(@RequestParam(value = "file", required = false) MultipartFile file) {
		String fileName = file.getOriginalFilename();
		Map<String, Object> map = new HashMap<String, Object>();
		try {
			InputStream fileInputStream = file.getInputStream();
			Deployment deployment = null;
			String extension = FilenameUtils.getExtension(fileName);
			if (extension.equals("zip") || extension.equals("bar")) {
				ZipInputStream zip = new ZipInputStream(fileInputStream);
				deployment = repositoryService.createDeployment().addZipInputStream(zip).deploy();
			} else {
				deployment = repositoryService.createDeployment().addInputStream(fileName, fileInputStream).deploy();
			}
			List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).list();
			for (ProcessDefinition processDefinition : list) {
				WorkflowUtils.exportDiagramToFile(repositoryService, processDefinition, "D:\\test\\");
			}
			return WebUtils.createJSONSuccess("部署成功");
		} catch (Exception e) {
			logger.error("error on deploy process, because of file input stream", e);
			return WebUtils.createJSONSuccess("部署失败");
		}

	}

	/**
	 * 初始化启动流程，读取启动流程的表单内容来渲染start form
	 */
	@ResponseBody
	@RequestMapping(value = "get-form/start/{processDefinitionId}")
	public Object findStartForm(HttpServletRequest request, @PathVariable("processDefinitionId") String processDefinitionId) throws Exception {
		Map<String, Object> map = new HashMap<String, Object>();
		// 根据流程定义ID读取外置表单
		Object startForm = formService.getRenderedStartForm(processDefinitionId);
		map.put("formdata", startForm);
		return WebUtils.createJSONSuccess("获取成功", map);
	}

	/**
	 * 办理任务，提交task的并保存form
	 */
	@ResponseBody
	@RequestMapping(value = "task/complete/{taskId}")
	public Object completeTask(@PathVariable("taskId") String taskId, RedirectAttributes redirectAttributes, HttpServletRequest request) {
		Map<String, Object> formProperties = new HashMap<String, Object>();
		formProperties.put("charge", 1);
		// 从request中读取参数然后转换
		Map<String, String[]> parameterMap = request.getParameterMap();
		Set<Entry<String, String[]>> entrySet = parameterMap.entrySet();
		for (Entry<String, String[]> entry : entrySet) {
			String key = entry.getKey();
			/*
			 * 参数结构：fq_reason，用_分割 fp的意思是form paremeter 最后一个是属性名称
			 */
			if (StringUtils.defaultString(key).startsWith("fp_")) {
				String[] paramSplit = key.split("_");
				formProperties.put(paramSplit[1], entry.getValue()[0]);
			}
		}
		logger.debug("start form parameters: {}", formProperties);
		LoginInfoBean currentuser = BusiContextExt.getLoginInfo();
		// 用户未登录不能操作，实际应用使用权限框架实现，例如Spring Security、Shiro等
		if (currentuser == null) {
			return "redirect:/login?timeout=true";
		}
		try {
			identityService.setAuthenticatedUserId(currentuser.getUserEO().getUserId().toString());
			tasksService.complete(taskId, formProperties);
			// formService.submitTaskFormData(taskId, formProperties);
		} finally {
			identityService.setAuthenticatedUserId(null);
		}
		redirectAttributes.addFlashAttribute("message", "任务完成：taskId=" + taskId);
		return WebUtils.createJSONSuccess("获取成功");
	}

	/**
	 * 读取启动流程的表单字段
	 */
	@ResponseBody
	@RequestMapping(value = "start-process/{processDefinitionId}")
	public Object submitStartFormAndStartProcessInstance(@PathVariable("processDefinitionId") String processDefinitionId, RedirectAttributes redirectAttributes, HttpServletRequest request) {
		Map<String, String> formProperties = new HashMap<String, String>();

		LoginInfoBean currentuser = BusiContextExt.getLoginInfo();
		// 用户未登录不能操作，实际应用使用权限框架实现，例如Spring Security、Shiro等
		if (currentuser == null) {
			return "redirect:/login";
		}
		try {
			identityService.setAuthenticatedUserId(currentuser.getUserEO().getUserName());
			formProperties.put("userId", "26_2");
			ProcessInstance processInstance = formService.submitStartFormData(processDefinitionId, formProperties);
			logger.debug("start a processinstance: {}", processInstance);
			redirectAttributes.addFlashAttribute("message", "启动成功，流程ID：" + processInstance.getId());
			String taskId = tasksService.createTaskQuery().executionId(processInstance.getId()).singleResult().getId();
			tasksService.setDueDate(taskId, new Date());
			tasksService.setOwner(taskId, currentuser.getUserEO().getUserId().toString());
		} finally {
			identityService.setAuthenticatedUserId(null);
		}
		return WebUtils.createJSONSuccess("启用成功");
	}

	/**
	 * task列表
	 *
	 * @param model
	 * @return
	 */
	@ResponseBody
	@RequestMapping(value = "task/list")
	public Object taskList(HttpServletRequest request, int page, int limit) {
		LoginInfoBean currentuser = BusiContextExt.getLoginInfo();
		PageInfo<Task> result = null;
		// List<Task> tasks = (List<Task>) result.getRoot();
		// List<ActTask> li = new ArrayList<ActTask>();
		// for (Task task : tasks) {
		// ActTask acttask = new ActTask();
		// acttask.setId(task.getId());
		// acttask.setTaskDefinitionKey(task.getTaskDefinitionKey());
		// acttask.setName(task.getName());
		// acttask.setProcessDefinitionId(task.getProcessDefinitionId());
		// acttask.setProcessInstanceId(task.getProcessInstanceId());
		// acttask.setPriority(task.getPriority());
		// acttask.setCreateTime(task.getCreateTime());
		// acttask.setDueDate(task.getDueDate());
		// acttask.setDescription(task.getDescription());
		// acttask.setOwner(task.getOwner());
		// li.add(acttask);
		// }
		// result.setRoot(li);
		return result;
	}

	/**
	 * task列表
	 *
	 * @param model
	 * @return
	 */
	@ResponseBody
	@RequestMapping(value = "task/mylist")
	public Object mytaskList(HttpServletRequest request, int page, int limit) {
		// CurrentUser currentuser = SecurityUserHolder.getCurrentUser();
		// ReturnResult result =
		// workFlowService.getMyTaskList(currentuser.getUserId(), page, limit);
		// List<Task> tasks = (List<Task>) result.getRoot();
		// List<ActTask> li = new ArrayList<ActTask>();
		// for (Task task : tasks) {
		// ActTask acttask = new ActTask();
		// acttask.setId(task.getId());
		// acttask.setTaskDefinitionKey(task.getTaskDefinitionKey());
		// acttask.setName(task.getName());
		// acttask.setProcessDefinitionId(task.getProcessDefinitionId());
		// acttask.setProcessInstanceId(task.getProcessInstanceId());
		// acttask.setPriority(task.getPriority());
		// acttask.setCreateTime(task.getCreateTime());
		// acttask.setDueDate(task.getDueDate());
		// acttask.setDescription(task.getDescription());
		// acttask.setOwner(task.getOwner());
		// li.add(acttask);
		// }
		// result.setRoot(li);
		// return result;
		return null;
	}

	/**
	 * 读取资源，通过流程ID
	 *
	 * @param resourceType
	 *            资源类型(xml|image)
	 * @param processInstanceId
	 *            流程实例ID
	 * @param response
	 * @throws Exception
	 */
	@ResponseBody
	@RequestMapping(value = "/resource/process-instance")
	public void loadByProcessInstance(@RequestParam("type") String resourceType, @RequestParam("pid") String processInstanceId, HttpServletResponse response) throws Exception {
		InputStream resourceAsStream = null;
		ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
		ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processInstance.getProcessDefinitionId()).singleResult();

		String resourceName = "";
		if (resourceType.equals("image")) {
			resourceName = processDefinition.getDiagramResourceName();
		} else if (resourceType.equals("xml")) {
			resourceName = processDefinition.getResourceName();
		}
		resourceAsStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), resourceName);
		byte[] b = new byte[1024];
		int len = -1;
		while ((len = resourceAsStream.read(b, 0, 1024)) != -1) {
			response.getOutputStream().write(b, 0, len);
		}
	}

	/**
	 * 输出跟踪流程信息
	 *
	 * @param processInstanceId
	 * @return
	 * @throws Exception
	 */
	@ResponseBody
	@RequestMapping(value = "/process/trace")
	public List<Map<String, Object>> traceProcess(@RequestParam("pid") String processInstanceId) throws Exception {
		List<Map<String, Object>> activityInfos = traceService.traceProcess(processInstanceId);
		return activityInfos;
	}

	/**
	 * 读取带跟踪的图片
	 */
	@ResponseBody
	@RequestMapping(value = "/process/trace/auto/{executionId}")
	public void readResource(@PathVariable("executionId") String executionId, HttpServletResponse response) throws Exception {
		ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(executionId).singleResult();
		BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
		List<String> activeActivityIds = runtimeService.getActiveActivityIds(executionId);
		// 使用spring注入引擎请使用下面的这行代码
		processEngineConfiguration = processEngine.getProcessEngineConfiguration();
		Context.setProcessEngineConfiguration((ProcessEngineConfigurationImpl) processEngineConfiguration);

		InputStream imageStream = processEngine
				.getProcessEngineConfiguration()
				.getProcessDiagramGenerator()
				.generateDiagram(bpmnModel, "png", activeActivityIds, new ArrayList(), processEngineConfiguration.getActivityFontName(), processEngineConfiguration.getLabelFontName(), null, null, 1.0);

		// 输出资源内容到相应对象
		byte[] b = new byte[1024];
		int len;
		while ((len = imageStream.read(b, 0, 1024)) != -1) {
			response.getOutputStream().write(b, 0, len);
		}
	}

}
